// ProjectName: go-learn-demo
// PackageName: redisybxb
// Description:
// Author: hy.bai
// Date: 2025-08-27 星期三 18:36

package redisybxb

import (
	"fmt"
	"github.com/redis/go-redis/v9"
	"log/slog"
)

// Sorted Set有序的集合：特点是有序, 且不可重复
// 每个元素有两个属性，一个是值，一个是Double类型的分数,结构体是：redis.Z

func RedisZAdd() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 为Sorted Set 有序集合：添加元素,元素的类型是Redis.Z类型,且支持插入任意个数据
	intCmd := client.ZAdd(*ctx, "name-z", redis.Z{
		Member: "name-hk-1",
		Score:  5,
	}, redis.Z{
		Member: "name-hk-2",
		Score:  4,
	}, redis.Z{
		Member: "name-hk-3",
		Score:  3.5,
	}, redis.Z{
		Member: "name-hk-4",
		Score:  2.5,
	}, redis.Z{
		Member: "name-hk-5",
		Score:  0.5,
	})
	fmt.Println(intCmd.Val())
}

func RedisZCard() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 返回有序集合(Sorted Set)元素个数
	intCmd := client.ZCard(*ctx, "name-z")
	fmt.Println("有序集合中元素的个数:", intCmd.Val())
}

func RedisZCount() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 返回有序集合(Sorted Set)中，分数是2<=分数<=3的元素个数。注意:"2","3"两个参数是字符串
	intCmd := client.ZCount(*ctx, "name-z", "2", "3")
	fmt.Println("有序集合中分数是1<=分数<=5的元素个数:", intCmd.Val())
}

func RedisZIncrBy() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 为有序集合(Sorted Set)中的指定元素的score值加上增量increment
	// 即: 如果原来name-hk-1的分值是5,那么执行ZIncrBy("name-z",1,"name-hk-1")后，name-hk-1的分值就是6
	intCmd := client.ZIncrBy(*ctx, "name-z", 1, "name-hk-1")
	fmt.Println("有序集合中name-hk-1的score值加上1:", intCmd.Val())
}

func RedisZRange() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 返回有序集合(Sorted Set)中指定区间的元素,区间的参数是start和stop,start和stop都是索引值,start和stop都是从0开始计数的
	// 0表示第一个元素,1表示第二个元素,以此类推;-1表示最后一个元素, -2表示倒数第二个元素,以此类推
	// sliceCmd := client.ZRange(*ctx, "name-z", 1, -2)
	sliceCmd := client.ZRange(*ctx, "name-z", 0, -1)
	vals := sliceCmd.Val()
	for _, val := range vals {
		fmt.Println("有序集合中指定区间的元素:", val)
	}
}

func RedisZRangeByScore() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 根据分数范围返回集合元素，元素根据分数从⼩到⼤排序，⽀持分⻚。
	// 初始化查询条件， Offset和Count⽤于分⻚
	opt := redis.ZRangeBy{
		Min:    "2", // 最⼩分数
		Max:    "4", // 最⼤分数
		Offset: 1,   // 类似sql的limit, 表示开始偏移量。注意:偏移量是结果集的索引值。
		Count:  2,   // ⼀次返回多少数据
	}
	sliceCmd := client.ZRangeByScore(*ctx, "name-z", &opt)
	vals := sliceCmd.Val()
	for _, val := range vals {
		fmt.Println("有序集合中指定分数范围的元素:", val)
	}
}

func RedisZRem() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 删除指定元素
	intCmd := client.ZRem(*ctx, "name-z", "name-hk-1")
	fmt.Println("有序集合中删除的元素数量:", intCmd.Val())
}

// 根据索引范围删除元素
func RedisZRemRangeByRank() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 根据索引范围删除元素。即：集合元素按分数排序，从最低分到⾼分，删除第1个元素到第2个元素。
	intCmd := client.ZRemRangeByRank(*ctx, "name-z", 1, 2)
	fmt.Println("有序集合中删除的元素数量:", intCmd.Val())
}

// 根据分数删除元素
func RedisZRemRangeByScore() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 根据分数删除元素。即：集合元素按分数排序，删除指定分数范围的元素
	intCmd := client.ZRemRangeByScore(*ctx, "name-z", "1", "3")
	fmt.Println("有序集合中删除的元素数量:", intCmd.Val())
}

func RedisZScore() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 返回指定元素的分值
	floatCmd := client.ZScore(*ctx, "name-z", "name-hk-2")
	fmt.Println("有序集合中指定元素的分值:", floatCmd.Val())
}

func RedisZRank() {
	client, ctx, err := InitClient()
	if err != nil {
		slog.Error(fmt.Sprintf("Redis Init Error: %s", err.Error()))
	}
	// 根据元素名，查询集合元素在集合中的排名，从0开始算，集合元素按分数从⼩到⼤排序
	intCmd := client.ZRank(*ctx, "name-z", "name-hk-2")
	fmt.Println("有序集合中指定元素的索引值:", intCmd.Val())
}

// 当前go文件的调用入口
func TestSortedSet() {
	RedisZRange()
}
